# 迭代对象(Iterable):
#   Python中任意的对象，只要它定义了可以返回一个迭代器的__iter__方法，或者定义了可以支持下标索引的__getitem__方法(这些双下划线方法会在其他章节中全面解释)，
#   那么它就是一个可迭代对象。简单说，可迭代对象就是能提供迭代器的任意对象。那迭代器又是什么呢
# 迭代器(Iterator) :
#   任意对象，只要定义了next(Python2) 或者__next__方法，它就是一个迭代器。就这么简单。现在我们来理解迭代(iteration)
# 迭代(Iteration):
#   用简单的话讲，它就是从某个地方（比如一个列表）取出一个元素的过程。当我们使用一个循环来遍历某个东西时，这个过程本身就叫迭代。
#   现在既然我们有了这些术语的基本理解，那我们开始理解生成器吧


# 生成器
# 生成器也是一种迭代器，但是你只能对其迭代一次。这是因为它们并没有把所有的值存在内存中，
# 而是在运行时生成值。你通过遍历来使用它们，要么用一个“for”循环，要么将它们传递给任意可以进行迭代的函数和结构。
# 大多数时候生成器是以函数来实现的。然而，它们并不返回一个值，而是yield(暂且译作“生出”)一个值
def generator_function(n):
    for i in range(n):
        yield i
    pass

print(type(generator_function(10)))
# for item in generator_function(10):
#     print(item)
#     pass
# 许多Python 2里的标准库函数都会返回列表，而Python 3都修改成了返回生成器，因为生成器占用更少的资源
# 计算斐波那契数列的生成器
def fibion(n):
    a = b = 1
    for i in range(n):
        yield a
        a, b = b, a + b
# for item in fibion(10000000):
#     print(item)
#     pass



# 传统方式超级耗内存
def fibon1(n):
    a = b = 1
    result = []
    for i in range(n):
        result.append(a)
        a, b = b, a + b
    return result
# for item in fibon1(10000000):
#     print(item)
#     pass




gen = generator_function(3)
print(next(gen))
# Output: 0
print(next(gen))
# Output: 1
print(next(gen))
# Output: 2
print(next(gen))
# Output: Traceback (most recent call last):
#            File "<stdin>", line 1, in <module>
#         StopIteration


my_string = "Yasoob"
next(my_string)
# Output: Traceback (most recent call last):
#      File "<stdin>", line 1, in <module>
#    TypeError: str object is not an iterator
# 这个异常说那个str对象不是一个迭代器。对，就是这样！它是一个可迭代对象，而不是一个迭代器。这意味着它支持迭代，但我们不能直接对其进行迭代操作


my_string = "Yasoob"
my_iter = iter(my_string)
next(my_iter)
# Output: 'Y'